package nkr1pt.popkorn.schedule;

import com.google.inject.Inject;
import java.util.logging.Level;
import java.util.logging.Logger;
import nkr1pt.popkorn.events.PopkornEvent;
import nkr1pt.popkorn.events.EventBus;
import nkr1pt.popkorn.events.EventObserver;
import nkr1pt.popkorn.events.PopkornEventFactory;
import nkr1pt.popkorn.exceptions.UnrecoverableException;
import nkr1pt.popkorn.properties.PropertyLoader;
import nkr1pt.popkorn.schedule.jobs.DownloadJob;
import nkr1pt.popkorn.schedule.triggers.ImmediateTrigger;
import nkr1pt.popkorn.schedule.triggers.NightlyTrigger;
import org.quartz.JobDetail;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.SchedulerFactory;
import org.quartz.Trigger;

/**
 *
 * @author Kristof Vanhaeren
 */
public class DownloadScheduler implements EventObserver {

    private EventBus eventBus;
    private PopkornEventFactory eventFactory;
    private Scheduler scheduler;
    private SchedulerMode schedulerMode;
    private PropertyLoader propertyLoader;
    private static final Logger logger = Logger.getLogger(DownloadScheduler.class.getName());

    @Inject
    public DownloadScheduler(EventBus eventBus, 
            final SchedulerFactory factory,
            final GuiceJobFactory jobFactory,
            PropertyLoader propertyLoader,
            PopkornEventFactory eventFactory) {
        
        this.eventBus = eventBus;
        this.eventFactory = eventFactory;
        this.eventBus.subscribe(this, PopkornEvent.EventType.PROPERTYFILE_CHANGED);
        this.propertyLoader = propertyLoader;
        try {
            scheduler = factory.getScheduler();
            scheduler.setJobFactory(jobFactory);
            scheduler.start();
        } catch (SchedulerException se) {
            handleSchedulerException(se);
        }
    }

    public void scheduleDownloads() {
        unscheduleDownloads();
        schedulerMode = SchedulerMode.valueOf(propertyLoader.getProperties().getScheduler().toUpperCase());
        switch (schedulerMode) {
            case IMMEDIATELY:
                scheduleImmediateDownloads();
                break;
            case NIGHTLY:
                scheduleNightlyDownloads();
                break;
            default:
                scheduleImmediateDownloads();
                break;
        }
    }

    public void unscheduleDownloads() {
        unscheduleImmediateDownloads();
        unscheduleNightlyDownloads();
    }

    public void scheduleNightlyDownloads() {
        try {
            JobDetail jobDetail = new JobDetail("downloadJob", null, DownloadJob.class);
            scheduler.scheduleJob(jobDetail, new NightlyTrigger());
            eventBus.fireEvent(eventFactory.newEvent(PopkornEvent.EventType.SCHEDULE_NIGHT));
            logger.log(Level.INFO, "Scheduled for downloading during the night");
        } catch (SchedulerException se) {
            handleSchedulerException(se);
        }
    }

    public void unscheduleNightlyDownloads() {
        try {
            scheduler.unscheduleJob("nightlyTrigger", null);
        } catch (SchedulerException se) {
            handleSchedulerException(se);
        }
    }

    public void scheduleImmediateDownloads() {
        try {
            JobDetail jobDetail = new JobDetail("downloadJob", null, DownloadJob.class);
            scheduler.scheduleJob(jobDetail, new ImmediateTrigger());
            eventBus.fireEvent(eventFactory.newEvent(PopkornEvent.EventType.SCHEDULE_IMMEDATE));
            logger.log(Level.INFO, "Scheduled for downloading immediately");
        } catch (SchedulerException se) {
            handleSchedulerException(se);
        }
    }

    public void unscheduleImmediateDownloads() {
        try {
            scheduler.unscheduleJob("immediateTrigger", null);
        } catch (SchedulerException se) {
            handleSchedulerException(se);
        }
    }

    public boolean isActive() {
        try {
            return scheduler != null && !scheduler.isShutdown();
        } catch (SchedulerException se) {
            return false;
        }
    }

    public String[] getJobNames() {
        try {
            return scheduler.getJobNames(null);
        } catch (SchedulerException se) {
            handleSchedulerException(se);
            return null;
        }
    }

    public String[] getTriggerNames() {
        try {
            return scheduler.getTriggerNames(null);
        } catch (SchedulerException se) {
            handleSchedulerException(se);
            return null;
        }
    }

    public Trigger[] getTriggersOfJob(String jobName) {
        try {
            return scheduler.getTriggersOfJob(jobName, null);
        } catch (SchedulerException se) {
            handleSchedulerException(se);
            return null;
        }
    }

    public void shutDown(boolean waitForJobsToComplete) {
        try {
            scheduler.shutdown(waitForJobsToComplete);
        } catch (SchedulerException ex) {
            handleSchedulerException(ex);
        }
    }

    public SchedulerMode getSchedulerMode() {
        return schedulerMode;
    }

    private void handleSchedulerException(SchedulerException se) {
        throw new UnrecoverableException(se.getMessage());
    }

    public void handleEvent(PopkornEvent event) {
        if (event.getType().equals(PopkornEvent.EventType.PROPERTYFILE_CHANGED)) {
            handlePropertyFileChangedEvent();
        }
    }

    private void handlePropertyFileChangedEvent() {
        scheduleDownloads();
    }

}